package org.wheel.plugins.toolkit.util;


import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import org.wheel.module.core.enm.NormalExceptionEnum;
import org.wheel.module.core.exception.NormalException;
import org.wheel.plugins.toolkit.sql.helper.SFunction;

import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <pre>
 * 函数式提取字段工具类
 * 参考：https://blog.csdn.net/weixin_46058921/article/details/125964198
 * 其他扩展，可参考：
 * https://www.jb51.net/article/224372.htm
 * http://voycn.com/index.php/article/mybatis-plusduqulambdaziduanfangshi
 * </pre>
 */
public class ConvertUtil {

    // 直接使用 LogFactory 创建日志对象，Hutool 会自动检测项目中的日志框架（优先级：Slf4j > Log4j > Logback > JDK Logging）
    private static final Log log = LogFactory.get();
    public static final String GET = "get";

    public static final String IS = "is";

    private static final Map<Class<?>, SerializedLambda> CLASS_LAMBDA_CACHE = new ConcurrentHashMap<>();

    /**
     * 转换方法引用为属性名
     *
     * @param fn
     * @return
     */
    public static String convertToFieldName(SFunction<?, ?> fn) {
        // 获取 SerializedLambda
        SerializedLambda serializedLambda = getSerializedLambda(fn);
        String methodName = serializedLambda.getImplMethodName();
        if (methodName.startsWith(GET)) {
            methodName = methodName.substring(3);
        } else if (methodName.startsWith(IS)) {
            methodName = methodName.substring(2);
        } else {
            throw new IllegalArgumentException("无效的getter方法：" + methodName);
        }
        return StrUtil.lowerFirst(methodName);
    }

    /**
     * 只是简单的获取字段名并将驼峰命名转成下划线
     * @param fn
     * @return
     */
    public static String convertToDBName(SFunction<?, ?> fn) {
        String propertyName = convertToFieldName(fn);
        Class<?> clazz = getLambdaClass(fn);
        if(clazz == null){
            return null;
        }
        Field info = Arrays.stream(ReflectUtil.getFields(clazz))
                .filter(i->i.getName().equals(propertyName))
                .findAny().orElse(null);
        if(info != null){
            return StrUtil.toUnderlineCase(info.getName());
        }else{
            return null;
        }
    }

    public static SerializedLambda getSerializedLambda(SFunction<?, ?> lambda) {
        try {
            Method method = lambda.getClass().getDeclaredMethod("writeReplace");
            method.setAccessible(true);
            return (SerializedLambda) method.invoke(lambda);
        } catch (Exception e) {
            throw new RuntimeException("Failed to extract SerializedLambda from lambda expression", e);
        }
    }

    public static <T> Class<T> getLambdaClass(SFunction<T, ?> fn) {
        SerializedLambda lambda = getSerializedLambda(fn);
        try {
            return (Class<T>) Class.forName(lambda.getImplClass().replace("/", "."));
        } catch (ClassNotFoundException e) {
            log.error("ClassNotFoundException: {}", e);
            throw new NormalException(NormalExceptionEnum.OPERATE_FAIL.getCode(), "获取lambda类失败");
        }
    }


    public static String convertToUnderline(SFunction<?, ?> fn) {
        String propertyName = convertToFieldName(fn);
        return StrUtil.toUnderlineCase(propertyName);
    }
}
